import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by eahn on 01/11/13.
 */
public class Main {

    private static Logger logger = LoggerFactory.getLogger(Main.class);

    private static Map<ComparisonResult.ResultType, Collection<ComparisonResult>> results = new HashMap<ComparisonResult.ResultType, Collection<ComparisonResult>>();

    private static String output;


    public static void main(String... args) throws IOException {

        // TEST
        //args = new String[] {"-delta", "src/test/resources/TestCase/CHARLY4/vor_update",  "src/test/resources/TestCase/CHARLY4/FTP", "src/test/resources/TestCase/CHARLY4/delta_update"};

        if (args.length < 3 || args.length > 4) {

            System.out.println("USAGE: ");
            System.out.println("   Comparator: -compare <src> <dest> ");
            System.out.println("   Comparator with check Backup: -backup <src> <dest> <backup>  ");
            System.out.println("   Comparator with check Delta: -delta <src> <dest> <delta>  ");
            System.out.println("   Uncompressor: -uncompress <src> <dest>  ");

            return;
        }

        //
        // Init class Attributes
        //
        output = "out/result-" + getTimeStamp() + "/";
        File src = null;
        File dest = null;

        //********************************************************************************************************
        //
        //     Flag: -compare
        //     Compare the contents of 2 directories and writes the result in 4 txt files
        //
        //********************************************************************************************************
        if (args[0].equalsIgnoreCase("-compare")) {

            if (args.length == 3) {

                src = new File(args[1]);
                dest = new File(args[2]);

                compareDirs(src, dest);

            } else {

                System.out.println("Wrong parameters!");

            }

            return;
        }


        //********************************************************************************************************
        //
        //     Flag: -uncompress
        //     Copy the folder args[1] to args[2] and uncompress all zip files and delete all the zip and property files
        //
        //********************************************************************************************************
        if (args[0].equalsIgnoreCase("-uncompress")) {

            if (args.length == 3) {

                src = new File(args[1]);
                dest = new File(args[2]);

                File tmp = dest;

                logger.debug("Copying " + src.getAbsolutePath() + " to " + tmp.getAbsolutePath());
                FileUtils.copyDirectory(src, tmp, true);
                ZipUncompressorWalker uncompressor = new ZipUncompressorWalker(tmp);
                uncompressor.start();

                DeleteFilesWalker deleteFilesWalker = new DeleteFilesWalker(".zip"/*, ".properties"*/);
                deleteFilesWalker.start(tmp);


            } else {

                System.out.println("Wrong parameters!");
            }


            return;
        }


        //********************************************************************************************************
        //
        //     Flag: -backup
        //     Compare the contents of 2 directories and writes the result in 4 txt files
        //     Check if the "differents" exist in the "backup" directory
        //
        //********************************************************************************************************
        if (args[0].equalsIgnoreCase("-backup")) {

            if (args.length == 4) {

                src = new File(args[1]);
                dest = new File(args[2]);

                compareDirs(src, dest);

                File backup = new File(args[3]);

                // check the differents against the backup folder

                List<ComparisonResult> filesToCheck = new LinkedList<ComparisonResult>();
                filesToCheck.addAll(results.get(ComparisonResult.ResultType.DIFFERENT));

                FileChecker checker = new FileChecker(backup);
                List<ComparisonResult> conflicteds = checker.checkAndReturnConflicteds(filesToCheck);

                collectionToFile(conflicteds, output + "non_existent_in_backup.txt");


                // check the equals against the backup folder

                FileNotExistChecker notExistChecker = new FileNotExistChecker(backup);
                filesToCheck = new LinkedList<ComparisonResult>();
                filesToCheck.addAll(results.get(ComparisonResult.ResultType.EQUAL));
                filesToCheck.addAll(results.get(ComparisonResult.ResultType.ONLY_IN_SRC));

                conflicteds = notExistChecker.checkAndReturnConflicteds(filesToCheck);

                collectionToFile(conflicteds, output + "existent_in_backup_but_should_not.txt");

            } else {

                System.out.println("Wrong parameters!");

            }

            return;

        }


        //********************************************************************************************************
        //
        //     Flag: -delta
        //     Compare the contents of 2 directories and writes the result in 4 txt files
        //     Check if the "differents" and "only_in_src" exist in the "delta" directory
        //
        //********************************************************************************************************
        if (args[0].equalsIgnoreCase("-delta")) {

            if (args.length == 4) {

                //
                // interchanged the files parameter, the second param is used as SOURCE Dir
                //
                src = new File(args[2]);
                dest = new File(args[1]);

                compareDirs(src, dest);

                File delta = new File(args[3]);

                List<ComparisonResult> filesToCheck = new LinkedList<ComparisonResult>();
                filesToCheck.addAll(results.get(ComparisonResult.ResultType.DIFFERENT));
                filesToCheck.addAll(results.get(ComparisonResult.ResultType.ONLY_IN_SRC));

                FileChecker checker = new FileChecker(delta);
                List<ComparisonResult> conflicteds = checker.checkAndReturnConflicteds(filesToCheck);

                collectionToFile(conflicteds, output + "non_existent_in_delta.txt");

            } else {

                System.out.println("Wrong parameters!");

            }

            return;

        }


    }


    /**
     * Compare 2 directories and generate the result in txt files
     *
     * @throws IOException
     */
    private static void compareDirs(File src, File dest) throws IOException {

        DirectoryComparisonWalker searcher = new DirectoryComparisonWalker(src, dest);
        searcher.start(Boolean.TRUE);

        Collection<ComparisonResult> equals = searcher.getResult(ComparisonResult.ResultType.EQUAL);
        collectionToFile(equals, output + "equals.txt");

        Collection<ComparisonResult> differents = searcher.getResult(ComparisonResult.ResultType.DIFFERENT);
        collectionToFile(differents, output + "differents.txt");

        Collection<ComparisonResult> onlyInSrcs = searcher.getResult(ComparisonResult.ResultType.ONLY_IN_SRC);
        collectionToFile(onlyInSrcs, output + "only_in_src.txt");

        Collection<ComparisonResult> onlyInDests = searcher.getResult(ComparisonResult.ResultType.ONLY_IN_DEST);
        collectionToFile(onlyInDests, output + "only_in_dest.txt");


        results.put(ComparisonResult.ResultType.EQUAL, equals);
        results.put(ComparisonResult.ResultType.DIFFERENT, differents);
        results.put(ComparisonResult.ResultType.ONLY_IN_SRC, onlyInSrcs);
        results.put(ComparisonResult.ResultType.ONLY_IN_DEST, onlyInDests);

    }


    public static void collectionToFile(Collection<ComparisonResult> col, String filename) {

        File result = null;
        BufferedWriter writer = null;

        try {
            result = new File(filename);
            result.getParentFile().mkdirs();

            writer = new BufferedWriter(new FileWriter(result, false));

            for (ComparisonResult o : col) {
                writer.write(o.toString());
                writer.newLine();
            }

        } catch (IOException e) {
            logger.debug("Can not write file " + result.getAbsolutePath());
            throw new RuntimeException("Can not write file " + result.getAbsolutePath(), e);
        } finally {

            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    throw new RuntimeException("Can not close file " + result.getAbsolutePath(), e);
                }
            }
        }

        logger.debug(">>> Result File '{}'", result.getName());

    }


    private static String getTimeStamp() {
        Calendar c = Calendar.getInstance();
        DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
        return df.format(c.getTime());
    }
}
